package values

import (
	"errors"
	"fmt"
	"time"
)

// ToBase 转换 any 目标为 TypeBase 泛型T, 如果转换失败, 则返回T零值
//   - TypeBase: string | bool | int | int8 | int16 | int32 | int64 |	uint | uint8 | uint16 | uint32 | uint64 |	float32 | float64 |	time.Time | time.Duration | time.Month | time.Weekday
//   - 注意: 处理time.Time | time.Duration 类型时, 如待转换的any目标是日期时间字符串且未包含时区信息时则使用UTC时区
func ToBase[T TypeBase](v any) T {
	to, _ := ToBaseE[T](v)
	return to
}

// ToBase 转换 any 目标为 TypeBase 泛型T, 如果转换失败, 则返回defaultVal指定的默认值
//   - TypeBase: string | bool | int | int8 | int16 | int32 | int64 |	uint | uint8 | uint16 | uint32 | uint64 |	float32 | float64 |	time.Time | time.Duration | time.Month | time.Weekday
//   - 注意: 处理time.Time | time.Duration 类型时, 如待转换的any目标是日期时间字符串且未包含时区信息时则使用UTC时区
func ToBaseD[T TypeBase](v any, defaultVal T) T {
	if to, err := ToBaseE[T](v); err == nil {
		return to
	}
	return defaultVal
}

// ToBaseE 转换 any 目标为 TypeBase 泛型T, 如果转换失败, 则返回T零值和错误信息
//   - TypeBase: string | bool |int | int8 | int16 | int32 | int64 |	uint | uint8 | uint16 | uint32 | uint64 |	float32 | float64 |	time.Time | time.Duration | time.Month | time.Weekday
//   - 注意: 处理time.Time | time.Duration 类型时, 如待转换的any目标是日期时间字符串且未包含时区信息时则使用UTC时区
func ToBaseE[T TypeBase](v any) (toVal T, err error) {
	return _toBaseE[T](UnderefVal(v))
}

// _toBaseE 转换 any 目标为 TypeBase 泛型T, 如果转换失败, 则返回T零值和错误信息
//   - 待转换目标为已经UnderefVal处理的参数
func _toBaseE[T any](v any) (toVal T, err error) {
	if v == nil {
		return toVal, errors.New("ToBaseE: nil pointer not support")
	}
	var vs any
	switch any(toVal).(type) {
	default:
		err = fmt.Errorf("ToBaseE: unable to (%#v) type %T of type %T", v, v, toVal)
	case string:
		vs, err = ToStringE(v)
	case bool:
		vs, err = ToBoolE(v)
	case int:
		vs, err = ToIntE(v)
	case int8:
		vs, err = ToInt8E(v)
	case int16:
		vs, err = ToInt16E(v)
	case int32:
		vs, err = ToInt32E(v)
	case int64:
		vs, err = ToInt64E(v)
	case uint:
		vs, err = ToUintE(v)
	case uint8:
		vs, err = ToUint8E(v)
	case uint16:
		vs, err = ToUint16E(v)
	case uint32:
		vs, err = ToUint32E(v)
	case uint64:
		vs, err = ToUint64E(v)
	case float32:
		vs, err = ToFloat32E(v)
	case float64:
		vs, err = ToFloat64E(v)
	case time.Time:
		vs, err = ToTimeE(v)
	case time.Duration:
		vs, err = ToTimeDurationE(v)
	case time.Month:
		vs, err = ToTimeMonthE(v)
	case time.Weekday:
		vs, err = ToTimeWeekdayE(v)
	case error:
		if vError, ok := v.(error); ok {
			vs = vError
		} else {
			err = fmt.Errorf("ToBaseE: unable to(%#v) type %T of type error", v, v)
		}
	case any:
		vs = v
	}
	if err == nil {
		if to, ok := vs.(T); ok {
			return to, nil
		}
		return toVal, fmt.Errorf("ToBaseE: unable to(%#v) type %T of type %T", v, v, toVal)
	}
	return toVal, fmt.Errorf("ToBaseE: %s", err.Error())
}
